package cn.hill4j.tool.core.obj.collect;


import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
/**
  * ClassName: CollectionTools
  * Description: 集合处理器工具类
  * @author hillchen
  * Date: 2023/8/7
  * @version: 1.0
  */
public class CollectionTools {
    /**
     * @author hillchen
     * Description 将对象集合拼接成字符串起来
     * Date: 2023/8/7 23:12
     * @param <S> 集合元素类型
     * @param source 原始数据集合
     * @param toStrFun 原始集合数据原始转换成String字符串的转换器
     * @param joinStr 字符串拼接间隔符
     * @return String
     **/
    public static <S> String toJoinString(Collection<S> source,Function<S,String> toStrFun,String joinStr){
        if (isEmpty(source)){
            return "";
        }
        return source.stream().map(toStrFun).collect(Collectors.joining(joinStr));
    }
    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <T> 目标对象类型
     * @param source 源集合
     * @param excluder 对象屏蔽器，excluder执行结构返回true则转存到目标集合中
     * @return List
     */
    public static <S,T> List<T> exclude(Collection<S> source, Predicate<S> excluder){
        if (isEmpty(source)){
            return Collections.emptyList();
        }
        return collect(source,excluder, CollectionConstant.PROTOTYPE,Collectors.toList());
    }
    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <T> 目标对象类型
     * @param source 源集合
     * @param converter 对象转换器
     * @param excluder 对象屏蔽器，excluder执行结构返回true则转存到目标集合中
     * @return List
     */
    public static <S,T> List<T> toList(Collection<S> source, Predicate<S> excluder, Function<S,T> converter){
        if (isEmpty(source)){
            return Collections.emptyList();
        }

        return collect(source,excluder,converter,Collectors.toList());
    }
    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <T> 目标对象类型
     * @param source 源集合
     * @param converter 对象转换器
     * @return List
     */
    public static <S,T> List<T> toList(Collection<S> source, Function<S,T> converter){
        return toList(source, CollectionConstant.NO_EXCLUDER,converter);
    }
    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <T> 目标对象类型
     * @param source 源集合
     * @param converter 对象转换器
     * @param excluder 对象屏蔽器，excluder执行结构返回true则转存到目标集合中
     * @return Set
     */
    public static <S,T> Set<T> toSet(Collection<S> source, Predicate<S> excluder, Function<S,T> converter){
        if (isEmpty(source)){
            return Collections.emptySet();
        }

        return collect(source,excluder,converter,Collectors.toSet());
    }
    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <T> 目标对象类型
     * @param source 源集合
     * @param converter 对象转换器
     * @return Set
     */
    public static <S,T> Set<T> toSet(Collection<S> source, Function<S,T> converter){
        return toSet(source, CollectionConstant.NO_EXCLUDER,converter);
    }

    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <K> 目标map健值类型
     * @param <T> 目标map数据类型
     * @param source 源集合
     * @param keyFun 健值获取器:从原集合元素中提取
     * @param converter 对象转换器
     * @return Map
     */
    public static <S,K,T> Map<K,T> toMap(Collection<S> source, Function<S,K> keyFun, Function<S, T> converter){
        ToMapContext<S,K,T> context = new ToMapContext<S, K, T>(keyFun,source,converter);
        return context.toMap();
    }



    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <K> 目标map健值类型
     * @param <T> 目标map数据类型
     * @param source 源集合
     * @param keyFun 集合健值获取器:从目标集合元素中提取
     * @param converter 对象转换器
     * @return Map
     */
    public static <S,K,T> Map<K,T> toMapByTarget(Collection<S> source, Function<T,K> keyFun, Function<S, T> converter){
        ToMapContext<S,K,T> context = new ToMapContext<S, K, T>(source,converter,keyFun);
        return context.toMap();
    }


    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <K> 目标map健值类型
     * @param <T> 目标map数据类型
     * @param context map转换器上下文
     * @return Map
     */
    public static <S,K,T> Map<K,T> toMap(ToMapContext<S,K,T> context){
        return context.toMap();
    }
    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <K> 目标map健值类型
     * @param <T> 目标map数据类型
     * @param source 源集合
     * @param converter 对象转换器
     * @param keyFun key值提取器
     * @param valExcluder 元素过滤器
     * @param keyExcluder key值过滤器
     * @return Map
     */
    public static <S,K,T> Map<K,T> toMap(Collection<S> source, Function<S, T> converter, BiFunction<S,T,K> keyFun, BiPredicate<S,T> valExcluder, Predicate<K> keyExcluder){
        ToMapContext<S,K,T> context = new ToMapContext<S, K, T>(source,converter,keyFun)
                .setValExcluder(valExcluder)
                .setKeyExcluder(keyExcluder);
        return context.toMap();
    }

    /**
     * 集合类型转换
     * @param <S> 源对象类型
     * @param <T> 目标对象类型
     * @param source 源集合
     * @param converter 对象转换器
     * @param excluder 对象屏蔽器，excluder执行结构返回true则转存到目标集合中
     */
    private static <S,T,C> C collect(Collection<S> source, Predicate<S> excluder, Function<S,T> converter, Collector<T,?, C> collector){
       return source.stream()
               .filter(e -> !excluder.test(e))
               .map(converter)
               .collect(collector);
    }
    /**
     * @author hillchen
     * Description 从集合中获取元素，如果不存在，则通过元素构造器构造元素、放到map中并返回
     * Date: 2023/8/7 23:35
     * @param <K> 源对象类型
     * @param <V> 目标对象类型
     * @param map map集合
     * @param key 元素key值
     * @param valCreator 元素构造器
     * @return V
     **/
    public static <K,V> V getOrDefaultAndPut(Map<K,V> map, K key, Function<K,V> valCreator){
        if (map.containsKey(key)){
            return map.get(key);
        }
        V v = valCreator.apply(key);
        map.put(key,v);
        return v;
    }
    /**
     * @author hillchen
     * Description 从集合中获取元素，如果不存在，则通过元素构造器构造元素、放到map中并返回
     * Date: 2023/8/7 23:40
     * @param <K> 源对象类型
     * @param <V> 目标对象类型
     * @param map map集合
     * @param key 元素key值
     * @param supplier 元素构造器
     * @return V
     **/
    public static <K,V> V getOrDefaultAndPut(Map<K,V> map, K key, Supplier<V> supplier){
        if (map.containsKey(key)){
            return map.get(key);
        }
        V v = supplier.get();
        map.put(key,v);
        return v;
    }

    /**
     * @author hillchen
     * Description 判断集合是否为空对象或空集合
     * Date: 2023/8/7 23:41
     * @param <S> 集合元素类型
     * @param source 集合
     * @return boolean 如果为空对象或空集合，则返回true
     **/
    public static <S> boolean isEmpty(Collection<S> source){
        return Objects.isNull(source) || source.isEmpty();
    }

    /**
     * @author hillchen
     * Description 判断集合是否为空对象或空集合
     * Date: 2023/8/7 23:41
     * @param <K> map key类型
     * @param <V> map value类型
     * @param source 集合
     * @return boolean 如果为空对象或空集合，则返回true
     **/
    public static <K,V> boolean isEmpty(Map<K,V> source){
        return Objects.isNull(source) || source.isEmpty();
    }

    /**
     * @author hillchen
     * Description 判断集合是否为空对象或空集合
     * Date: 2023/8/7 23:41
     * @param <S> 集合元素类型
     * @param source 集合
     * @return boolean 如果为空对象或空集合，则返回false
     **/
    public static <S> boolean notEmpty(Collection<S> source){
        return !isEmpty(source);
    }

    /**
     * @author hillchen
     * Description 判断集合是否为空对象或空集合
     * Date: 2023/8/7 23:41
     * @param <K> map key类型
     * @param <V> map value类型
     * @param source 集合
     * @return boolean 如果为空对象或空集合，则返回false
     **/
    public static <K,V> boolean notEmpty(Map<K,V> source){
        return !isEmpty(source);
    }
    /**
      *
      * ClassName: ToMapContext
      * Description: map转换器上下文
      * @author hillchen
      * @param <S> 源对象类型
      * @param <K> 目标map健值类型
      * @param <T> 目标map数据类型
      * Date: 2023/8/7
      * @version: 1.0
      */
    public static class ToMapContext<S, K,T> {
        /**
         * 原始原始集合
         */
        private Collection<S> source;
        /**
         * 原始元素转换成目标元素的转换器
         */
        private Function<S,T> converter;
        /**
         * 从元素元素或目标元素提取出目标map的key值获取器
         */
        private BiFunction<S,T,K> keyGetter;
        /**
         * 元素过滤器，如果过滤器返回为true，则表示需要过滤
         */
        private BiPredicate<S,T> valExcluder = CollectionConstant.NO_BI_EXCLUDER;
        /**
         * key值过滤器，如果过滤器返回为true，则表示需要过滤
         */
        private Predicate<K> keyExcluder = CollectionConstant.NO_EXCLUDER;
        /**
         * @author hillchen
         * Description map转换器构造函数
         * Date: 2023/8/7 23:26
         * @param keyGetter 从原始元素提取出目标map的key值获取器
         * @param sources 原始原始集合
         * @param converter 转换器
         **/
        public ToMapContext( Function<S, K> keyGetter,Collection<S> sources, Function<S, T> converter) {
            this(sources,converter,(e,t) -> keyGetter.apply(e));
        }
        /**
         * @author hillchen
         * Description map转换器构造函数
         * Date: 2023/8/7 23:26
         * @param sources 原始原始集合
         * @param converter 转换器
         * @param keyGetter 从目标元素提取出目标map的key值获取器
         **/
        public ToMapContext(Collection<S> sources, Function<S, T> converter, Function<T, K> keyGetter) {
            this(sources,converter,(e,t) -> keyGetter.apply(t));
        }
        /**
         * @author hillchen
         * Description map转换器构造函数
         * Date: 2023/8/7 23:26
         * @param sources 原始原始集合
         * @param converter 转换器
         * @param keyGetter 从元素元素或目标元素提取出目标map的key值获取器
         **/
        public ToMapContext(Collection<S> sources, Function<S, T> converter, BiFunction<S,T, K> keyGetter) {
            this.source = sources;
            this.converter = converter;
            this.keyGetter = keyGetter;
        }
        /**
         * @author hillchen
         * Description 设置元素过滤器
         * Date: 2023/8/7 23:27
         * @param valExcluder 元素过滤器
         * @return ToMapContext
         **/
        public ToMapContext setValExcluder(BiPredicate<S,T> valExcluder){
            this.valExcluder = valExcluder;
            return this;
        }
        /**
         * @author hillchen
         * Description 设置key值过滤器
         * Date: 2023/8/7 23:28
         * @param keyExcluder key值过滤器
         * @return ToMapContext
         **/
        public ToMapContext setKeyExcluder(Predicate<K> keyExcluder) {
            this.keyExcluder = keyExcluder;
            return this;
        }
        /**
         * @author hillchen
         * Description 转换成map
         * Date: 2023/8/7 23:32
         * @return Map
         **/
        public Map<K,T> toMap(){
            if (CollectionTools.isEmpty(source)){
                return Collections.emptyMap();
            }
            Map<K,T> result = new HashMap<>();
            for (S s: source){
                T t = converter.apply(s);
                if (!valExcluder.test(s,t)){
                    K key = keyGetter.apply(s,t);
                    if (!keyExcluder.test(key)){
                        result.put(key,t);
                    }
                }
            }
            return result;
        }

    }

}
